package mq

import (
	"encoding/json"
	"gitee.com/injoyai/goutil/net/conn"
	"gitee.com/injoyai/goutil/net/tcp"
	"log"
	"sync"
	"time"
)

type Config struct {
	Port    int               //端口
	Debug   bool              //debug
	Timeout time.Duration     //超时时间
	App     map[string]string //app信息
}

type Server struct {
	*Config                         //配置
	ser     *tcp.Server             //服务
	mLogin  map[string][]*conn.Conn //登录字典
	mu      sync.RWMutex            //app信息锁
	retry   chan *Msg               //发送失败的信息
}

func (this *Server) Run() error {
	return this.ser.Run()
}

func (this *Server) HasConn(key string, c *conn.Conn) bool {
	this.mu.RLock()
	defer this.mu.RUnlock()
	list, ok := this.mLogin[key]
	if ok {
		for _, v := range list {
			if &v == &c {
				return true
			}
		}
	}
	return false
}

func (this *Server) GetConn(key string) []*conn.Conn {
	this.mu.RLock()
	defer this.mu.RUnlock()
	return this.mLogin[key]
}

func (this *Server) AddConn(key string, c *conn.Conn) {
	this.mu.Lock()
	defer this.mu.Unlock()
	this.mLogin[key] = append(this.mLogin[key], c)
}

func (this *Server) DelConn(key string, c *conn.Conn, err ...error) {
	this.mu.Lock()
	defer this.mu.Unlock()
	if list, ok := this.mLogin[key]; ok {
		for i, v := range list {
			if &v == &c {
				c.Close(err...)
				this.mLogin[key] = append(list[:i], list[i+1:]...)
			}
		}
	}
}

func (this *Server) getSecret(key string) string {
	this.mu.RLock()
	defer this.mu.RUnlock()
	if this.App == nil {
		return ""
	}
	return this.App[key]
}

func NewServer(cfg *Config) *Server {
	s := tcp.NewServer(cfg.Port)
	data := &Server{
		Config: cfg,
		ser:    s,
		mLogin: make(map[string][]*conn.Conn),
		retry:  make(chan *Msg),
	}
	s.SetTimeout(cfg.Timeout)
	s.Debug(cfg.Debug)
	s.SetDealFunc(func(m *conn.Msg) {
		msg := new(Msg)
		if err := json.Unmarshal(m.Bytes(), msg); err != nil {
			log.Printf("[错误] %s", err.Error())
			m.Conn().Close(err)
		} else {

			//日志记录
			log.Printf("[日志][%s][%s] %s", m.Key(), msg.Type, m.String())

			switch msg.Type {

			case typeLogin:

				//登录响应
				msgBack := &Msg{
					Type: typeLogin,
					Data: newMsgRes(200, "登录成功").bytes(),
				}
				//校验签名
				l := msg.toLogin()
				appSecret := data.getSecret(l.AppKey)
				if err := l.check(appSecret); err != nil {
					msgBack.Data = newMsgRes(-1, err.Error()).bytes()
					m.Conn().SendBytes(msgBack.bytes())
					m.Conn().Close(err)
				} else {
					//储存链接,保留别名
					data.AddConn(msg.ClientOf, m.Conn())
					//响应登录成功信息
					m.Conn().SendBytes(msgBack.bytes())
				}

			case typeMsg:

				//消息响应
				msgBack := &Msg{
					Type: typeMsg,
					Data: newMsgRes(200, "登录成功").bytes(),
				}

				//判断是否登录
				if !data.HasConn(msg.ClientOf, m.Conn()) {
					msgBack.Data = newMsgRes(-1, "未登录").bytes()
				}

				//获取目的地,妆发数据
				for _, c := range data.GetConn(msg.ClientTo) {
					c.SendBytes(msg.bytes())
				}

			}

		}

	})
	return data
}
